/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: message.cpp
 *
 * Purpose: message default implementation
 *
 * Developer:
 *   wen.gu , 2021-07-04
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "icpp/com/message.h"

#include "icpp/com/serializer.h"
#include "icpp/com/deserializer.h"

namespace icpp
{
namespace com
{
/******************************************************************************
 **    MACROS
 ******************************************************************************/
/**the size of :  |message id(4byte) |length(4byte)|session id(2byte)|flags(2byte)|protocol version(1byte)|interface version(1byte)|message type(1byte)|return code(1byte)| */
#define MSG_ID_SIZE sizeof(uint32_t)
#define MSG_LEN_SIZE sizeof(uint32_t)
#define MSG_SESSION_SIZE sizeof(uint16_t)
#define MSG_FLAGS_SIZE   sizeof(uint16_t)
#define MSG_PROTO_VER_SIZE sizeof(uint8_t)
#define MSG_INTERFACE_VER_SIZE sizeof(uint8_t)
#define MSG_MSG_TYPE_SIZE sizeof(uint8_t)
#define MSG_RET_CODE_SIZE sizeof(uint8_t)

#define HEADER_BASE_SIZE  (MSG_ID_SIZE + MSG_LEN_SIZE + MSG_SESSION_SIZE + MSG_FLAGS_SIZE + MSG_PROTO_VER_SIZE + MSG_INTERFACE_VER_SIZE + MSG_MSG_TYPE_SIZE + MSG_RET_CODE_SIZE)
#define HEADER_PREFIX_SIZE (MSG_ID_SIZE + MSG_LEN_SIZE)
/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/



/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/

Message::Message(MessageType type)
:type_(type)
{
    /** todo something */
}

Message::Message::~Message()
{
    /** todo something */
}


MessageId Message::message_id() const {return message_id_;}
uint32_t Message::length() const 
{ 
    return HEADER_PREFIX_SIZE + payload_size();
}

SessionId Message::session_id() const {return session_id_;}
MessageFlags Message::flag() const { return flags_;}
uint8_t Message::protocol_version() const { return protocol_version_;}
uint8_t Message::interface_version() const { return interface_version_;}
MessageType Message::type() const { return type_;}
uint8_t Message::code() const {return code_;}
PayloadPtr Message::payload() const {return payload_;}
uint32_t   Message::send_time() const {return send_time_ms_; }
uint32_t   Message::alive_time() const { return alive_time_ms_; }

void Message::set_message_id(MessageId id) { message_id_ = id; }
void Message::set_session_id(SessionId id) {session_id_ = id;}
void Message::set_flag(MessageFlags flag) { flags_ = flag;}
void Message::set_protocol_version(uint8_t version) { protocol_version_ = version;}
void Message::set_interface_version(uint8_t version) {interface_version_ = version;}
void Message::set_type(MessageType type) {type_ = type;}
void Message::set_code(uint8_t code) {code_ = code;}
void Message::set_payload(PayloadPtr payload) {payload_ = payload;}
void Message::set_send_time(uint32_t send_time_ms) { send_time_ms_ = send_time_ms; }
void Message::set_alive_time(uint32_t alive_time_ms) { alive_time_ms_ = alive_time_ms; }


uint32_t Message::header_size() const
{
    uint32_t size = HEADER_BASE_SIZE;
    if (flags_ & MessageFlags::kTimeSensitive)
    {
        size += sizeof(uint32_t) + sizeof(uint32_t);/** add the length of send time and alive time */
    }

    return size;
}


uint32_t Message::payload_size()  const { return (payload_ != nullptr) ? payload_->size(): 0;}
uint32_t Message::message_size() const {return header_size() + length();}


void Message::set_opaque(MessageOpaque::MessageOpaquePtr opaque_ptr)
{
    opaque_ = opaque_ptr;
}

MessageOpaque::MessageOpaquePtr Message::opaque()
{
    return opaque_;
}

void Message::set_owner(EndpointId the_owner) /** who owned current message */
{
    owner_ = the_owner;
}

EndpointId Message::owner()
{
    return owner_;
}


//static 
Message::MessagePtr Message::make(MessageType type)
{
    return std::make_shared<Message>(type);
}


bool Message::serialize(Serializer *ser_to) const
{
    /**
     * the protocol of message data:
     * |message id(4 byte)|session id(2 byte)|flag(2 byte)|protocol version(1byte)|interface version(1byte)|type(1byte)|code(1byte)|length(4 byte)|payload(variable length)|[option]checksum(crc16 2byte)|
     */ 
    if (ser_to)
    {       
        if (payload_)
        {
            if (flag() & MESSAGE_FLAG_ENABLE_CHECKSUM)
            {
                return ser_to->serialize(message_id_,session_id_, flags_, protocol_version_, interface_version_, (uint8_t)type_, (uint8_t)code_, length(), *(payload_.get()), checksum()); 
            }
            else
            {
                return ser_to->serialize(message_id_,session_id_, flags_, protocol_version_, interface_version_, (uint8_t)type_, (uint8_t)code_, length(), *(payload_.get())); 
            }           
        }
        else
        {
            return ser_to->serialize(message_id_,session_id_, flags_, protocol_version_, interface_version_, (uint8_t)type_, (uint8_t)code_, 0);
        }      
    }

    return false;

} 

bool Message::deserialize(Deserializer *deser_from) 
{
    /**
     * the protocol of message data:
     * |message id(4 byte)|session id(2 byte)|flag(2 byte)|protocol version(1byte)|interface version(1byte)|type(1byte)|code(1byte)|length(4 byte)|payload(variable length)|[option]checksum(crc16 2byte)|
     */  
    if (deser_from)
    {
        payload_ = std::make_shared<Payload>();

        if (payload_ == nullptr)
        {
            return false;
        }

        uint32_t len = 0;

        if (deser_from->deserialize(message_id_, session_id_, flags_, protocol_version_, interface_version_, (uint8_t&)type_, code_, len))
        {
            if (len > 0)
            {                
                if (!deser_from->deserialize(*(payload_.get())))
                {
                    payload_ = nullptr;
                    return false;
                }

                if (flags_ & MESSAGE_FLAG_ENABLE_CHECKSUM)
                {
                    if (!deser_from->deserialize(checksum_))
                    {
                        payload_ = nullptr;
                        return false;
                    }     

                    /** todo, is need check the checksum?? */               
                }
            }

            return true;    
        }

        payload_ = nullptr;
    }

    return false;
}

} /** namespace com */
} /** namespace icpp */
